
// Copyright (C) 2009 Luca Piccioni
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;

namespace Derm.BindGen
{
	/// <summary>
	/// Specification parser.
	/// </summary>
	class SpecParser
	{
		#region Constructors

		/// <summary>
		/// Static constructor.
		/// </summary>
		static SpecParser()
		{
			// Load C# type mappings
			LoadCsTypeMap("../../Specs/Cs.tm");
			// Load user function configuration
			LoadUserFunctionConfiguration("../../Specs/Functions.spec");
		}

		/// <summary>
		/// SpecParser constructor.
		/// </summary>
		/// <param name="sDict">
		/// A <see cref="SpecDictionary"/> used for
		/// </param>
		public SpecParser(SpecDictionary sDict)
		{
			Debug.Assert(sDict != null);

			// Store dictionary instance
			mSpecDict = sDict;
		}

		/// <summary>
		/// Specification dictionary used for parsing.
		/// </summary>
		private SpecDictionary mSpecDict;

		#endregion

		#region C# Language Bindings

		/// <summary>
		/// Check whether a string is a C# keyword.
		/// </summary>
		/// <param name="key"></param>
		/// <returns></returns>
		public static bool IsCsKeywork(string key)
		{
			Debug.Assert(key != null);

			foreach (string k in sCsKeywords)
				if (k == key) return (true);
			return (false);
		}

		public static string GetCsUnsafeType(string cType)
		{
			Debug.Assert(cType != null);

			if (sCsTypeMap.ContainsKey(cType) == false) {
				Console.WriteLine("Warning: type {0} not mapped. Defaulting to IntPtr.", cType);
				return ("IntPtr");
			} else {
				return (sCsTypeMap[cType]);
			}
		}

		public static string GetCsUnsafeArrayType(string cType)
		{
			Debug.Assert(cType != null);

			string sType = GetCsUnsafeType(cType);

			if      (sType == "void")	return ("IntPtr");
			else if (sType == "char")	return ("string");
			else if (sType == "string")	return ("string[]");
				return (sType+"*");
		}
		
		public static string GetCsSafeType(string cType)
		{
			string sType = GetCsUnsafeType(cType);

			return (sType.Replace("*", "[]"));
		}

		public static string GetCsSafeArrayType(string cType)
		{
			string sType = GetCsSafeType(cType);

			if      (sType == "void")	return ("object");
			else if (sType == "char")	return ("string");
			else if (sType == "string")	return ("string[]");
			else
				return (sType+"[]");
		}

		/// <summary>
		/// Load C# type mappings.
		/// </summary>
		/// <param name="path">
		/// A <see cref="System.String"/> specifying the path to find the file containing the mapping
		/// between specification types to C# types.
		/// </param>
		private static void LoadCsTypeMap(string path)
		{
			StreamReader sr = new StreamReader(path);

			while (sr.EndOfStream == false) {
				string sLine = sr.ReadLine();
				string[] sLineTokens;
				string sType, cType;

				// Trim white spaces
				sLine = sLine.Trim(' ', '\t');
				// Skip empty lines
				if (sLine.Length == 0)
					continue;
				// Skip comment lines
				if (sLine.StartsWith("#") == true)
					continue;

				sLineTokens = sLine.Split(new char[] { '=' }, StringSplitOptions.RemoveEmptyEntries);
				sType = sLineTokens[0].Trim();
				cType = sLineTokens[1].Trim();

				sCsTypeMap[sType] = cType;
			}

			sr.Close();
		}

		/// <summary>
		/// Map between OpenGL C type definitions with C# unsafe type definitions.
		/// </summary>
		/// <remarks>
		/// This map is used to translate OpenGL specification types (already processed with specification
		/// typemap, i.e. gl.tm) with C# unsafe types. To get relative safe types see GetCsSafeType.
		/// </remarks>
		private static Dictionary<string,string> sCsTypeMap = new Dictionary<string,string>();
		/// <summary>
		/// C# keywords.
		/// </summary>
		private static readonly string[] sCsKeywords = new string[] {
			"abstract", "event", "new", "struct",
			"as", "explicit", "null", "switch",
			"base", "extern", "object", "this",
			"bool", "false", "operator", "throw",
			"break", "finally", "out", "true",
			"byte",  "fixed", "override", "try",
			"case", "float", "params", "typeof",
			"catch", "for", "private", "uint",
			"char", "foreach", "protected", "ulong",
			"checked", "goto", "public", "unchecked",
			"class", "if", "readonly", "unsafe",
			"const", "implicit", "ref", "ushort",
			"continue", "in", "return", "using",
			"decimal", "int", "sbyte", "virtual",
			"default", "interface", "sealed", "volatile",
			"delegate", "internal", "short", "void",
			"do", "is", "sizeof", "while",
			"double", "lock", "stackalloc", 
			"else", "long", "static", 
			"enum", "namespace", "string", 
		};

		#endregion

		#region Specification Parsing

		/// <summary>
		/// Parse a specification (GL, WGL, XGL).
		/// </summary>
		/// <param name="spec">
		/// A <see cref="System.String"/> specifying the function specification file path.
		/// </param>
		/// <param name="extspec">
		/// A <see cref="System.String"/> specifying the extended function specification file path. This
		/// parameter can be set to null in the case the file is not provided.
		/// </param>
		/// <param name="enums">
		/// A <see cref="System.String"/> specifying the enumeration specification file path.
		/// </param>
		/// <param name="extenums">
		/// A <see cref="System.String"/> specifying the extended enumeration specification file path. This
		/// parameter can be set to null in the case the file is not provided.
		/// </param>
		/// <param name="typemap">
		/// A <see cref="System.String"/> specifying the type mapping used in the specification files.
		/// </param>
		public void ParseSpec(string spec, string extspec, string enums, string extenums, string typemap, bool deprecation)
		{
			// Type map
			ParseTypeMapSpecFile(typemap);

			// Enumerations
			ParseEnumSpecFile(enums);
			ParseEnumExtSpecFile(extenums);
			if (deprecation == true)
				RemoveDeprecatedEnumValues();
#if false
			foreach (string ext in mSpecDict.Extensions) {
				RenameEnumExt("_" + ext, false, true);
			}
			RemoveEmptyEnums();
			RenameEnumArb();
			RenameEnumExt();
#endif

			// Function specification
			ParseSpecFile(spec);
			if (extspec != null)
				ParseSpecFile(extspec);
			if (deprecation == true)
				RemoveDeprecatedFunction();
#if false
			foreach (string ext in mSpecDict.Extensions)
				RenameFunctionExt(ext, (ext == "ARB"), true);
#endif
			RenameDefaultFunctions();
		}

		/// <summary>
		/// Parse specification type map.
		/// </summary>
		/// <param name="path">
		/// A <see cref="System.String"/> specifying the type mapping used in the specification files.
		/// </param>
		/// <remarks>
		/// After having called this routine, it is possible to call the routine MapType for obtaining
		/// implementation types (for parameters, returned values) from the specification abstract types.
		/// </remarks>
		private void ParseTypeMapSpecFile(string path)
		{
			StreamReader sr = new StreamReader(path);
			uint sLineIndex = 0;

			while (sr.EndOfStream == false) {
				string sLine = sr.ReadLine(); sLineIndex++;
				string[] sLineTokens;
				string vType, mType;

				// Trim white spaces
				sLine.Trim(' ', '\t');
				// Skip empty lines
				if (sLine.Length == 0)
					continue;
				// Skip comment lines
				if (sLine.StartsWith("#") == true)
					continue;

				// Parse spec line
				sLineTokens = sLine.Split(new char[] {','}, StringSplitOptions.RemoveEmptyEntries);
				for (int i = 0; i < sLineTokens.Length; i++)
					sLineTokens[i] = sLineTokens[i].TrimStart();

				if (sLineTokens.Length < 6)
					throw new Exception("type map line not composed by two main types (line "+sLineIndex+")");

				vType = sLineTokens[0];
				mType = sLineTokens[3];

				// Store type mapping
				mTypeMap[vType] = mType;
			}

			sr.Close();
		}

		/// <summary>
		/// Parse enumeration specification type map.
		/// </summary>
		/// <param name="path">
		/// A <see cref="System.String"/> specifying the enumeration specification file path.
		/// </param>
		/// <remarks>
		/// This routine collect every enumeration (and associated values) specified in the file
		/// corresponding to the <paramref name="path"/> passed as parameter.
		/// For more details, <seealso cref="ParseEnumSpecFileDelegate"/>.
		/// </remarks>
		private void ParseEnumSpecFile(string path)
		{
			ProcessEnumSpec(path, ParseEnumSpecFileDelegate);
		}

		/// <summary>
		/// Parse extended enumeration specification type map.
		/// </summary>
		/// <param name="path">
		/// A <see cref="System.String"/> specifying the enumeration specification file path.
		/// </param>
		/// <remarks>
		/// This routine collect every enumeration (and associated values) specified in the file
		/// corresponding to the <paramref name="path"/> passed as parameter.
		/// For more details, <seealso cref="ParseEnumExtSpecFileDelegate"/>.
		/// </remarks>
		private void ParseEnumExtSpecFile(string path)
		{
			ProcessEnumSpec(path, ParseEnumExtSpecFileDelegate);
		}

		private void ParseSpecFile(string path)
		{
			StreamReader sr = new StreamReader(path);
			uint lineIndex = 0;

			Console.WriteLine("Parsing function specification {0}", path);

			// Read first line
			string sourceLine = sr.ReadLine();
			if (sourceLine == null)
				throw new ArgumentException("end of file", "path");
			lineIndex++;
			
			while (sr.EndOfStream == false) {
				Function sFunct;
				string[] sLineTokens;

				#region Blank Sections

				// Skip comment lines and empty lines
				if (IsIgnoredStatement(sourceLine) == true) {
					sourceLine = sr.ReadLine(); lineIndex++;
					continue;
				}

				if (sourceLine == null)
					throw new ArgumentException("end of file", "path");

				// Parse spec line
				sLineTokens = sourceLine.Split(new char[] {' ','\t'}, StringSplitOptions.RemoveEmptyEntries);
				// Check whether it is an ignored statement
				if ((sLineTokens.Length == 0) || (IsIgnoredStatement(sLineTokens[0]) == true)) {
					sourceLine = sr.ReadLine(); lineIndex++;
					continue;
				}

				#endregion

				#region Function Definition

				// Store function signature
				sFunct = new Function(sourceLine);
				sFunct.ImportNamePrefix = FunctPrefix;

				// Check for renaming function (renaming specified in Functions.spec)
				//
				// The renaming operation is done here to avoid mismatching caused by later
				// function name processing.
				if (sFunctRename.ContainsKey(sFunct.ImportName) == true)
					sFunct.ImplementationName = sFunctRename[sFunct.ImportName];

				#region Function Properties

				// Set function properties
				sourceLine = sr.ReadLine();
				if (sourceLine == null)
					throw new ArgumentException("end of file", "path");
				lineIndex++;
				while ((sr.EndOfStream == false) && (sourceLine.Length != 0)) {
					string[] sFunctPropTokens = sourceLine.Split(new char[] {' ','\t'}, StringSplitOptions.RemoveEmptyEntries);

					if (sFunctPropTokens.Length == 0)
						break;	// No more properties

					if        (sFunctPropTokens[0] == "return") {
						// return	<type>
						sFunct.ReturnedValueType = MapType(sFunctPropTokens[1]);
						Debug.Assert(sFunct.ReturnedValueType != null);
					} else if (sFunctPropTokens[0] == "param") {
						Function.Parameter sParam;

						// param	<pname>		<ptype> [in|out] [value|array] [# COMPSIZE[<EXP>]]
						if (sFunctPropTokens.Length < 5)
							throw new Exception("function parameter not described with at least 5 tokens (line "+lineIndex+")");

						// Get function parameter
						sParam = sFunct.GetParameter(sFunctPropTokens[1]);
						if (sParam == null)
							throw new Exception("parameter \""+sFunctPropTokens[1]+"\" not defined in function signature");
						Debug.Assert(sParam.SpecMappedTypeName == null);

						// Parameter type
						sParam.SetTypeName(sFunctPropTokens[2], MapType(sFunctPropTokens[2]));
						Debug.Assert(sParam.SpecMappedTypeName != null);
						// Parameter direction
						if      (sFunctPropTokens[3] == "out")
							sParam.MDirection = Function.Parameter.MarshalDirection.Out;
						else if (sFunctPropTokens[3] == "in")
							sParam.MDirection = Function.Parameter.MarshalDirection.In;
						else
							throw new Exception("function parameter "+sFunctPropTokens[1]+" has unknown direction "+sFunctPropTokens[3]+" (line "+lineIndex+")");

						// Array parameter
						if (sFunctPropTokens[4] == "value")
							sParam.Indirection = Function.Parameter.IndirectionMode.ByValue;
						else if (sFunctPropTokens[4] == "array")
							sParam.Indirection = Function.Parameter.IndirectionMode.ByArray;
						else if (sFunctPropTokens[4].StartsWith("array["))	// XXX
							sParam.Indirection = Function.Parameter.IndirectionMode.ByArray;
						else if (sFunctPropTokens[4] == "reference") {
							if (sParam.SpecMappedTypeName != "GLvoid") {
								sParam.Indirection = Function.Parameter.IndirectionMode.ByArray;
								sParam.FDirection = Function.Parameter.FuncDirection.Ref;
								sParam.MDirection = Function.Parameter.MarshalDirection.In;
							}
						} else
							throw new Exception("function parameter "+sFunctPropTokens[1]+" has unknown value type "+sFunctPropTokens[4]+" (line "+lineIndex+")");

						if ((sParam.Indirection == Function.Parameter.IndirectionMode.ByArray) && (sParam.MDirection == Function.Parameter.MarshalDirection.Out)) {
							// Try to determine possible array elements counts
							if ((sFunctPropTokens.Length > 5) && (sFunctPropTokens[5].StartsWith("[")))
								sParam.SetArraySize(sFunctPropTokens[5]);
						}

					} else if (sFunctPropTokens[0] == "deprecated") {
						// deprecated	<glversion>
						sFunct.Deprecate();
					} else if (sFunctPropTokens[0] == "category") {
						// category		<catstring>
						sFunct.Category = sFunctPropTokens[1];
					} else if (sFunctPropTokens[0] == "alias") {
						// alias		<aliasstring>
						sFunct.Alias = sFunctPropTokens[1];
					} else if (sFunctPropTokens[0] == "version") {
						// alias		<aliasstring>
						sFunct.Version = sFunctPropTokens[1];
					}

					// Next property (or blank line terminating properties)
					sourceLine = sr.ReadLine(); lineIndex++;
				}

				#endregion

				#region Postprocess Function Properties

				// Obtain inter-parameter informations (after having parsed every property)
				foreach (Function.Parameter p in sFunct.GetParameters()) {
					p.ParseArraySize(sFunct);
					p.CheckForAlternatives(this);
				}

				#endregion

				// Collect function
				if (mFunctions.ContainsKey(sFunct.ImportName) == false) {
					bool exclude = false;

					// Exclude function by name (only implementation)
					if (sFunctExclude.ContainsKey(sFunct.ImportName) == true)
						sFunct.ImplementationExcluded = true;
					// Exclude function by category (extentions)
					if ((sFunct.Category != null) && (sFunctExclude.ContainsKey(sFunct.Category) == true))
						exclude = true;

					if (exclude == false)
						mFunctions.Add(sFunct.ImportName, sFunct);
				}

				#endregion
			}
			
			sr.Close();

			foreach (Function function in mFunctions.Values) {
				if (function.HasOverloadedImplementations) {
					foreach (Function overloadFunction in mFunctions.Values) {
						if ((overloadFunction.ImplementationName == function.ImplementationName) && (ReferenceEquals(function, overloadFunction) == false)) {
							// Possible overload collision
							function.CheckForOverloadCollisions(overloadFunction);
						}
					}
				}
			}

			Console.WriteLine("Parsed {0} functions", mFunctions.Count);
		}

		/// <summary>
		/// Process a parsed enumeration value.
		/// </summary>
		/// <param name="eEnumName">
		/// A <see cref="System.String"/> which defines the currently parsed enumeration name.
		/// </param>
		/// <param name="eFwdEnum">
		/// 
		/// </param>
		/// <param name="eValueName">
		/// A <see cref="System.String"/> which defines the currently parsed enumeration value name.
		/// </param>
		/// <param name="eValue">
		/// A <see cref="Enum.Value"/> which defines the value associated with the enumeration value name.
		/// </param>
		/// <remarks>
		/// This routine is used as delegate (called by ProcessEnumSpec) for processing enumerations. It removes
		/// default prefixes from enumeration value names and it doesn't include duplicated enumeration values (in
		/// the same enumeration).
		/// </remarks>
		private void ParseEnumSpecFileDelegate(string eEnumName, string eFwdEnum, string eValueName, Enum.Value eValue)
		{
			Enum eItem = null;
			bool vDup = false;;

			// Get enumeration (existing or not)
			try {
				eItem = mEnums[eEnumName];
			} catch (KeyNotFoundException) {
				// Enumeration not found... create a new one
				eItem = new Enum(eEnumName);
				// Add enumeration to enum database
				mEnums.Add(eEnumName, eItem);
			}

			// Check duplicated value in each registered enumeration
			foreach (Enum e in mEnums.Values) {
				foreach (Enum.Value v in e.Values) {
					if ((v.IsForwarded == false) && (v.Name == eValue.Name)) {
						vDup = true;
						break;
					}
				}
			}

			// Apped value to enumeration
			if (vDup == false) {
				// Check for deprecated value
				if (eValue.ValueString.StartsWith("0x") == false) {
					foreach (Enum e in mEnums.Values) {
						foreach (Enum.Value v in e.Values) {
							if ((v.IsForwarded == false) && (v.Name == eValue.ValueString)) {
								if (v.IsDeprecated == true)
									eValue.Deprecate();
							}
						}
					}
				}
				// Register enumeration value
				eItem.AddValue(eValue);
			}
		}

		/// <summary>
		/// Process a parsed enumeration value.
		/// </summary>
		/// <param name="eEnumName">
		/// A <see cref="System.String"/> which defines the currently parsed enumeration name.
		/// </param>
		/// <param name="eFwdEnum">
		/// 
		/// </param>
		/// <param name="eValueName">
		/// A <see cref="System.String"/> which defines the currently parsed enumeration value name.
		/// </param>
		/// <param name="eValue">
		/// A <see cref="Enum.Value"/> which defines the value associated with the enumeration value name.
		/// </param>
		/// <remarks>
		/// This routine is very similar to <see cref="ParseEnumSpecFileDelegate"/>, but it assumes that most
		/// of parsed enumeration values are already present in this SpecParser (because <see cref="ParseEnumSpecFile"/>
		/// was already called).
		/// Additionally, the extended enumeration specification mark deprecated enumeration ending with "DEPRECATED"
		/// string the enumeration name. Using this information, this routine can mark an enumeration (and all its
		/// associated enumeration values as deprecated, for later remotion.
		/// </remarks>
		private void ParseEnumExtSpecFileDelegate(string eEnumName, string eFwdEnum, string eValueName, Enum.Value eValue)
		{
			if (eEnumName.EndsWith("DEPRECATED") == true) {
				// Remove deprecated enumerations
				foreach (Enum e in mEnums.Values) {
					foreach (Enum.Value v in e.Values) {
						if      ((v.Name == eValue.Name))
							v.Deprecate();
						else if ((v.ValueString != null) && (v.ValueString == eValue.Name))
							v.Deprecate();
						else if ((v.ValueString != null) && (v.ValueString == eValue.ValueString))
							v.Deprecate();
					}
				}
			} else {
				// Append unknown enumeration
				ParseEnumSpecFileDelegate(eEnumName, null, eValueName, eValue);
			}
		}

		/// <summary>
		/// Delegate definition for parsing an enumeration value.
		/// </summary>
		/// <param name="eEnumName">
		/// A <see cref="System.String"/> which defines the currently parsed enumeration name.
		/// </param>
		/// <param name="eFwdEnum">
		/// A <see cref="System.String"/> which defines the forwarded enumeration containing the
		/// enumeration value which has been considered to not duplicate the enumeration value (in
		/// specification file is determined by using the "use" statement).
		/// </param>
		/// <param name="eValueName">
		/// A <see cref="System.String"/> which defines the currently parsed enumeration value name.
		/// </param>
		/// <param name="eValue">
		/// A <see cref="Enum.Value"/> which defines the value associated with the enumeration value name.
		/// </param>
		private delegate void ParseEnumValue(string eEnumName, string eFwdEnum, string eValueName, Enum.Value eValue);

		private void ProcessEnumSpec(string path, ParseEnumValue cb)
		{
			StreamReader sr = new StreamReader(path);
			string sLine;
			uint sLineIndex = 0;

			Console.WriteLine("Parsing enum specification {0}", path);

			// Read first line
			sLine = sr.ReadLine(); sLineIndex++;
			
			while (sr.EndOfStream == false) {
				string[] sLineTokens;

				#region Blank Sections

				// Skip comment lines and empty lines
				if (IsIgnoredStatement(sLine) == true) {
					sLine = sr.ReadLine(); sLineIndex++;
					continue;
				}

				// Parse spec line
				sLineTokens = sLine.Split(' ');
				// Check whether it is an ignored statement
				if (IsIgnoredStatement(sLineTokens[0]) == true) {
					sLine = sr.ReadLine(); sLineIndex++;
					continue;
				}

				#endregion

				// Handle definition statements

				if        (sLineTokens[1] == "define:") {		// Define statement - Skipped
					// Skip extensions list
					for (;;) {
						sLine = sr.ReadLine(); sLineIndex++;
						if (sLine.Length == 0)
							break;
					}

					sLine = sr.ReadLine(); sLineIndex++;
					continue;
				} else if (sLineTokens[1] == "enum:") {			// Define enumeration: "<EnumName> enum:"
					string eEnumName = sLineTokens[0];

					for (; sr.EndOfStream == false;) {
						bool done = false;

						do {
							string[] eLineTokens;

							// Read enumeration value
							if ((sLine = sr.ReadLine()) == null) {
								sLineIndex++;
								break;
							}
							sLine = sLine.Trim();

							// Skip comment lines and empty lines
							if (IsIgnoredStatement(sLine) == true)
								continue;

							// Parse enumeration value
							eLineTokens = sLine.Split(new char[] {' ','\t'}, StringSplitOptions.RemoveEmptyEntries);

							// Skip comment lines and empty lines
							if (IsIgnoredStatement(eLineTokens[0]) == true)
								continue;

							if (eLineTokens.Length > 0) {
								if ((eLineTokens[1] != "enum:") && ((eLineTokens.Length >= 3) && ((eLineTokens[0] == "use") || (eLineTokens[1] == "=")))) {
									Enum.Value eValue;
									string eValueName = null, eValueString = null, eFwdEnum = null;
									bool useStatement = (eLineTokens[0] == "use");

									#region Name & Value Determination

									if (useStatement == false) {
										// Extract enumeration symbol name
										eValueName = eLineTokens[0];
										// Extract enumeration symbol value
										eValueString = eLineTokens[2];
									} else {
										// Extract enumeration symbol name
										eValueName = eLineTokens[2];
										// Use statement doesn't define value
										eValueString = null;
										// Enumeration with holds definition of forwarded value
										eFwdEnum = eLineTokens[1];
									}

									#endregion

									#region Name & Value Prefix Remotion

									// Remove prefixe on symbol name
									eValueName = TrimEnumValuePrefix(eValueName);

									// Add "_" if enumeration value name starts with non-allowed characters
									if ((eValueName[0] >= '0') && (eValueName[0] <= '9'))
										eValueName = "_"+eValueName;
									// Remove prefix on symbol value
									if (eValueString != null) {
										foreach (string p in sTrimEnumValuePrefix) {
											if (eValueString.StartsWith(p) == true) {
												eValueString = TrimEnumValuePrefix(eValueString);
												break;
											}
										}
									}

									#endregion

									#region Enumeration Value Parsing

									// Excluded enum
									if (sEnumExclude.ContainsKey(eEnumName) == false) {
										// Append value to enumeration
										if (useStatement == false) {
											// Compose enumeration value
											eValue = new Enum.Value(eValueName, eValueString);
											// Process enumeration
											cb(eEnumName, null, eValueName, eValue);
										} else {
											// Compose enumeration value
											eValue = new Enum.Value(eFwdEnum, eValueName, "value "+eValueName+" of enumeration "+eFwdEnum);
											// Process enumeration
											cb(eEnumName, eValueName, eValueString, eValue);
										}
									}

									#endregion
								} else
									break;
							}
						} while (done == false);

						break;
					}
				} else {
					sLine = sr.ReadLine();
					 sLineIndex++;
				}
			}

			Console.WriteLine("Parsed {0} enumerations", mEnums.Count);
		}

		/// <summary>
		/// Determine whether a line is a ignored statement.
		/// </summary>
		/// <param name="state"></param>
		/// <returns>
		/// It returns true whether the parameter <paramref name="state"/> is an empty line, a
		/// comment line or an ignored statement (listed in <see cref="sIgnoredStatements"/>).
		/// </returns>
		private static bool IsIgnoredStatement(string state)
		{
			// Trim white spaces
			state = state.Trim(' ', '\t');

			// Null string
			if (state.Length == 0)
				return (true);
			// Comment string
			if (state.StartsWith("#") == true)
				return (true);
			// Ignored special statement
			foreach (string s in sIgnoredStatements)
				if (s == state)
					return (true);

			return (false);
		}

		/// <summary>
		/// Trim enumeration prefix.
		/// </summary>
		/// <param name="eValue">
		/// A <see cref="System.String"/> which defines the enumeration value name to be trimmed.
		/// </param>
		/// <returns>
		/// It returns the <paramref name="eValue"/> value with trimmed hardcoded prefixes, defined
		/// in the static array <see cref="sTrimEnumValuePrefix"/>.
		/// </returns>
		private string TrimEnumValuePrefix(string eValue)
		{
			foreach (string p in sTrimEnumValuePrefix) {
				if (eValue.StartsWith(p) == true)
					return (eValue.Substring(p.Length));
			}

			return (eValue);
		}

		/// <summary>
		/// Ignored statements.
		/// </summary>
		private static readonly string[] sIgnoredStatements = new string[] {
			"passthru:", "passend:", "param:", "dlflags:", "vectorequiv:",
			"category:", "version:", "deprecated:", "extension:", "alias:",
			"offset:", "glfflags:", "beginend:", "subcategory:", "glextmask:",
			"required-props:", "newcategory:", "endcategory:",

			// WGL ignored statements
			"wglflags:",

			// GLX ignored statements
			"glxflags:", "glxvectorequiv:", "glxsingle:", "glxvendorglx:",
			"glxvendorpriv:", "glxopcode:", "glxropcode:"
		};

		/// <summary>
		/// Trimmed enumeration value prefixes.
		/// </summary>
		private static readonly string[] sTrimEnumValuePrefix = new string[] {
			"GL_", "WGL_", "GLX_"
		};

		/// <summary>
		/// Enumeration name prefix.
		/// </summary>
		/// <remarks>
		/// This <see cref="System.String"/> will be prepended to any enumeration value name parsed.
		/// </remarks>
		public string EnumPrefix = "";

		/// <summary>
		/// Function name prefix.
		/// </summary>
		/// <remarks>
		/// This <see cref="System.String"/> will be prepended to any function name parsed.
		/// </remarks>
		public string FunctPrefix = "";

		/// <summary>
		/// Specification namespace.
		/// </summary>
		/// <remarks>
		/// This <see cref="System.String"/> that specifies the scope of the implemented routines.
		/// </remarks>
		public string Namespace = "";

		/// <summary>
		/// Type map.
		/// </summary>
		/// <remarks>
		/// This type map is determine from specification files.
		/// </remarks>
		private Dictionary<string, string> mTypeMap = new Dictionary<string,string>();

		#endregion

		#region Specification Cooking

		/// <summary>
		/// Map a specification type using parsed type mappings.
		/// </summary>
		/// <param name="sType">
		/// A <see cref="System.String"/> which specify the specification abstract type.
		/// </param>
		/// <returns>
		/// It returns the mapped value of the parameter <paramref name="sType"/>. Since this
		/// mapped value is determine by the type map used for specification, it shall be
		/// mapped again for C# language, using the routines <see cref="GetCsUnsafeType"/>,
		/// <see cref="GetCsUnsafeArrayType"/>, <see cref="GetCsSafeType"/> or <see cref="GetCsSafeArrayType"/>.
		/// </returns>
		public string MapType(string sType)
		{
			try {
				return (mTypeMap[sType]);
			} catch (KeyNotFoundException) {
				return ("IntPtr");
			}
		}

		/// <summary>
		/// Map a specification function name to a binding function.
		/// </summary>
		/// <param name="function">
		/// A <see cref="System.String"/> specifying the function name found in the specification.
		/// </param>
		/// <returns>
		/// It returns a string specifying a binding function name corresponding to <paramref name="function"/>.
		/// </returns>
		public Function MapFunction(string function)
		{
			Function match;
			string ext, suffix, guess;
			int prefixLength = 0;

			// Direct match with the specification name
			if (Functs.ContainsKey(function) == true)
				return (Functs[function]);

			// Instead of building a specification name, guess possible implementation name

			// Prefix length
			if ((function.StartsWith("gl") == true) || (function.StartsWith("GL_"))) {
				prefixLength = 2;
			} else if ((function.StartsWith("wgl") == true) || (function.StartsWith("glX") == true) || (function.StartsWith("GL_") == true)) {
				prefixLength = 3;
			} else
				prefixLength = 4;
			
			// Remove prefix (no implementation name contains prefix)
			function = function.Substring(prefixLength);

			// Check whether the function ends with an extension
			ext = mSpecDict.EndsWithExtension(function);
			// Check whether the function ends with a suffix
			if (ext != null)
				suffix = mSpecDict.EndsWithSuffix(function.Substring(0, function.Length - ext.Length));
			else
				suffix = mSpecDict.EndsWithSuffix(function);

			if (ext != null) {
				if (suffix != null) {
					// Guess: function name without extention and without suffix
					guess = function.Substring(0, function.Length - ext.Length - suffix.Length);
					if ((match = GetFunctionByImplementationName(guess)) != null)
						return (match);

					// Guess: function name without extention and without suffix, but with appended the number of arguments
					for (int i = 1; i <= 4; i++) {
						string subguess = guess + i.ToString();

						if ((match = GetFunctionByImplementationName(subguess)) != null)
							return (match);
					}

					// Guess: function name with extention, but without suffix
					guess = function.Substring(0, function.Length - ext.Length);
					guess = guess.Substring(0, guess.Length - suffix.Length) + ext;
					if ((match = GetFunctionByImplementationName(guess)) != null)
						return (match);
				} else {
					// Guess: function name without extention and with suffix
					guess = function.Substring(0, function.Length - ext.Length);
					if ((match = GetFunctionByImplementationName(guess)) != null)
						return (match);
				}
			} else if (suffix != null) {
				// Guess: function name without suffix
				guess = function.Substring(0, function.Length - suffix.Length);
				if ((match = GetFunctionByImplementationName(guess)) != null)
					return (match);
			} else {
				// Clear name
				if ((match = GetFunctionByImplementationName(function)) != null)
					return (match);

				// Guess: function name without extention and without suffix, but with appended the number of arguments
				guess = function + "3";
				if ((match = GetFunctionByImplementationName(guess)) != null)
					return (match);
				guess = function + "4";
				if ((match = GetFunctionByImplementationName(guess)) != null)
					return (match);
				guess = function + "2";
				if ((match = GetFunctionByImplementationName(guess)) != null)
					return (match);
				guess = function + "1";
				if ((match = GetFunctionByImplementationName(guess)) != null)
					return (match);
			}

			return (null);
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="function"></param>
		/// <returns></returns>
		private Function GetFunctionByImplementationName(string function)
		{
			foreach (KeyValuePair<string, Function> pair in Functs) {
				if (pair.Value.ImplementationName == function)
					return (pair.Value);
			}

			return (null);
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="name"></param>
		/// <returns></returns>
		public Enum.Value MapEnumValue(string name)
		{
			Enum.Value value = null;
			string noprefixName = null;

			if (name.StartsWith(EnumPrefix) == true)
				noprefixName = name.Substring(EnumPrefix.Length);
			if ((name[0] >= '0') && (name[0] <= '9'))
				noprefixName = "_" + noprefixName;

			foreach (KeyValuePair<string, Enum> specEnum in Enums) {
				foreach (Enum.Value specEnumValue in specEnum.Value.Values) {
					if ((specEnumValue.Name == name) || (noprefixName != null && specEnumValue.Name == noprefixName)) {
						value = specEnumValue;
						break;
					}
				}

				if (value != null)
					break;
			}

			return (value);
		}

		/// <summary>
		/// Parsed enumerations.
		/// </summary>
		public IDictionary<string, Enum> Enums
		{
			get { return (mEnums); }
		}

		public void RemoveEmptyEnums()
		{
			LinkedList<string> rEnums = new LinkedList<string>();

			foreach (KeyValuePair<string, Enum> k in mEnums) {
				if (k.Value.ValuesCount == 0)
					rEnums.AddLast(k.Key);
			}

			Console.WriteLine("Removing {0} empty enumerations.", rEnums.Count);
			foreach (string s in rEnums)
				mEnums.Remove(s);
		}

		public void RemoveDeprecatedEnumValues()
		{
			LinkedList<Enum.Value> rValues = new LinkedList<Enum.Value>();
			int eValues = 0;

			foreach (KeyValuePair<string, Enum> k in mEnums) {
				Enum e = k.Value;
				
				// Remove by deprecation
				foreach (Enum.Value v in e.Values) {
					if (v.IsDeprecated == true)
						rValues.AddLast(v);
				}

				
				eValues += rValues.Count;
				foreach (Enum.Value v in rValues)
					e.RemoveValue(v);
				rValues.Clear();
			}

			Console.WriteLine("Removing {0} deprecated enumerations values.", eValues);
		}

		public void RenameEnumExt()
		{
			LinkedList<Enum.Value> rValues = new LinkedList<Enum.Value>();

			foreach (KeyValuePair<string, Enum> k1 in mEnums) {
				foreach (Enum.Value v1 in k1.Value.Values) {
					if (v1.Name.EndsWith("_EXT") == true) {
						Enum.Value dup;
						string eValueName = v1.Name.Substring(0, v1.Name.Length-"_EXT".Length), eValueString = v1.ValueString;

						if (((dup = GetEnumValue(eValueName)) == null) && ((dup = GetEnumValue(eValueName + "_ARB")) == null)) {
							// Remove EXT suffix from enum value name
							v1.ImplementationName = eValueName;
						} else {
							if (v1.ValueString == dup.ValueString) {
								// Collect alias
								v1.NameAliases.Add(v1.Name);
								// Remove ARB enum value
								rValues.AddLast(v1);
							}
						}
					}
				}

				foreach (Enum.Value v in rValues)
					k1.Value.RemoveValue(v);
				rValues.Clear();
			}
		}

		public void RenameEnumExt(string ext, bool rename)
		{
			LinkedList<Enum.Value> rValues = new LinkedList<Enum.Value>();

			// Iterate over every enumeration value
			foreach (KeyValuePair<string, Enum> k1 in mEnums) {
				foreach (Enum.Value v1 in k1.Value.Values) {
					string eValueName = v1.Name, eValueString = v1.ValueString;

					if (eValueName.EndsWith(ext) == true) {
						Enum.Value dup = null;
						string sName = eValueName.Substring(0, eValueName.Length-ext.Length);
						bool bDuplicated = false;

						if (sName.Length == 0)
							throw new Exception("Invalid enumeration name "+eValueName);

						if (((dup = GetEnumValue(sName)) != null) || ((ext != "_ARB") && (dup = GetEnumValue(sName + "_ARB")) != null)) {
							bDuplicated = true;
						}

						if (bDuplicated == true) {
							// Collect alias
							dup.NameAliases.Add(v1.Name);
							rValues.AddLast(v1);
						}
					}
				}

				foreach (Enum.Value v in rValues)
					k1.Value.RemoveValue(v);
				rValues.Clear();
			}

			if (rValues.Count > 0)
				Console.WriteLine("Removing {0} duplicated enumerations values ({1} extensions).", rValues.Count, ext);
		}

		private Enum.Value GetEnumValue(string name)
		{
			foreach (KeyValuePair<string, Enum> k1 in mEnums) {
				foreach (Enum.Value v1 in k1.Value.Values) {
					if (v1.Name == name)
						return (v1);
				}
			}

			return (null);
		}

		public IDictionary<string, Function> Functs
		{
			get { return (mFunctions); }
		}

		/// <summary>
		/// Remove functions.
		/// </summary>
		/// <remarks>
		/// This routine removes functions before they are processed for generating OpenGL bindings. The remotion
		/// is based on the deprecation state of the function, or it's based on function category in the case
		/// the category is listed in the removed functions (see Functions.spec file).
		/// </remarks>
		public void RemoveDeprecatedFunction()
		{
			LinkedList<string> rFuncts = new LinkedList<string>();

			foreach (KeyValuePair<string, Function> k in mFunctions) {
				Function f = k.Value;

				// Remove deprecated function
				if (f.IsDeprecated == true)
					rFuncts.AddLast(f.ImportName);
				// Remove function by category
				foreach (KeyValuePair<string, bool> s in sFunctExclude) {
					if ((f.ImportName == s.Key) || (f.Category == s.Key))
						rFuncts.AddLast(f.ImportName);
				}
			}

			if (rFuncts.Count > 0) {
				Console.WriteLine("Removing {0} deprecated and excluded functions!", rFuncts.Count);
				foreach (string s in rFuncts)
					mFunctions.Remove(s);
			}
		}

		public void RenameFunctionExt(string ext, bool rename, bool remove)
		{
			LinkedList<string> rFuncts = new LinkedList<string>();
			int rNames = 0;

			foreach (KeyValuePair<string, Function> k in mFunctions) {
				Function f = k.Value;
				string iName = f.ImportName;

				if (iName.EndsWith(ext) == true) {
					string sName = iName.Substring(0, iName.Length-ext.Length);
					bool sNameHasAlternative = false;

					
					if        (ext == "ARB") {
						// Core implementation has higher priority then ARB
						if (Functs.ContainsKey(sName) == true) {
							Function funct = Functs[sName];

							// Exception for removing ARB suffix: renamed function are not considered
							// a replacement of ARB routines (damn to WGL functions)

							if ((funct.ImportNamePrefix+funct.ImplementationName == sName) && (Functs[sName].IsCompatibleSubset(f) == true)) {
								Functs[sName].SubsetFunctions.Add(f);
								sNameHasAlternative = true;
							}
						}
					} else if (ext == "EXT") {
						// Core implementation has higher priority then EXT
						if ((Functs.ContainsKey(sName) == true) && (Functs[sName].IsCompatibleSubset(f) == true)) {
							Functs[sName].SubsetFunctions.Add(f);
							sNameHasAlternative = true;
						}
						// ARB implementation has higher priority then EXT
						if ((Functs.ContainsKey(sName+"ARB") == true) && (Functs[sName+"ARB"].IsCompatibleSubset(f) == true)) {
							Functs[sName+"ARB"].SubsetFunctions.Add(f);
							sNameHasAlternative = true;
						}
					} else {
						// Core implementation has higher priority then other extensions
						if ((Functs.ContainsKey(sName) == true) && (Functs[sName].IsCompatibleSubset(f) == true)) {
							Functs[sName].SubsetFunctions.Add(f);
							sNameHasAlternative = true;
						} else
						// ARB implementation has higher priority then other extensions
						if ((Functs.ContainsKey(sName+"ARB") == true) && (Functs[sName+"ARB"].IsCompatibleSubset(f) == true)) {
							Functs[sName+"ARB"].SubsetFunctions.Add(f);
							sNameHasAlternative = true;
						} else
						// EXT implementation has higher priority then other extensions
						if ((Functs.ContainsKey(sName+"EXT") == true) && (Functs[sName+"EXT"].IsCompatibleSubset(f) == true)) {
							Functs[sName+"EXT"].SubsetFunctions.Add(f);
							sNameHasAlternative = true;
						}
					}

					if (sNameHasAlternative == false) {
						if (rename == true) {
							// No name collision
							iName = f.ImplementationName;
							iName = iName.Substring(0, iName.Length-ext.Length);

							f.ImplementationName = iName;
							rNames++;
						}
					} else {
						// Another routine is preferable (EXT -> ARB or VENDOR -> EXT)
						f.HasSuperset = true;
					}
				}
			}

			foreach (string s in rFuncts) {
				foreach (KeyValuePair<string, Function> k in mFunctions) {
					Function f = k.Value;
					List<Function> rSubsetFunctions = new List<Function>();

					foreach (Function subset in f.SubsetFunctions)
						if (subset.ImplementationName == s)
							rSubsetFunctions.Add(subset);
					foreach (Function subset in rSubsetFunctions)
						f.SubsetFunctions.Remove(subset);
				}
				mFunctions.Remove(s);
			}

			if ((rename == true) && (rNames > 0))
				Console.WriteLine("Renamed {0} functions without {1} suffix!", rNames, ext);
			if ((remove == true) && (rFuncts.Count > 0))
				Console.WriteLine("Removing {0} functions with {1} suffix!", rFuncts.Count, ext);
		}

		public string RenameDefaultFunction(string name)
		{
			return (name);
		}

		/// <summary>
		/// Rename functions.
		/// </summary>
		public void RenameDefaultFunctions()
		{
			foreach (KeyValuePair<string, Function> k in mFunctions) {
				Function f = k.Value;
				string iName = f.ImplementationName;
				string ext;

				// Remove extension, if any
				if ((ext = mSpecDict.EndsWithExtension(iName)) != null)
					iName = iName.Substring(0, iName.Length - ext.Length);
				else
					ext = String.Empty;

				if (mSpecDict.EndsWithSuffix(iName) != null)
					f.ImplementationName = mSpecDict.RemoveSuffixes(iName) + ext;
			}
		}

		/// <summary>
		/// Determine whether a function shall be excluded.
		/// </summary>
		/// <param name="name">
		/// A <see cref="System.String"/> specifying the function name.
		/// </param>
		/// <returns>
		/// It returns true in the case the function shall be excluded, otherwise it
		/// returns false.
		/// </returns>
		private static bool IsExcludedFunction(string name)
		{
			return (sFunctExclude.ContainsKey(name));
		}

		private static void LoadUserFunctionConfiguration(string path)
		{
			StreamReader sr = new StreamReader(path);
			bool rename = false, exclude = false;

			while (sr.EndOfStream == false) {
				string sLine = sr.ReadLine();

				// Trim white spaces
				sLine = sLine.Trim(' ', '\t');
				// Skip empty lines
				if (sLine.Length == 0)
					continue;
				// Skip comment lines
				if (sLine.StartsWith("#") == true)
					continue;

				if (sLine == "rename:") {
					// Starting "rename" section
					rename = true;
					exclude = false;
				} else if (sLine == "exclude:") {
					// Starting "exclude" section
					rename = false;
					exclude = true;
				} else {
					if (rename == true) {
						string[] sLineTokens = sLine.Split(new char[] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries);

						sFunctRename.Add(sLineTokens[0], sLineTokens[1]);
					} else if (exclude == true) {
						sFunctExclude.Add(sLine, true);
						sEnumExclude.Add(sLine, true);
					}
				}
			}
			// End parsing
			sr.Close();
		}

		/// <summary>
		/// Enumerations parsed.
		/// </summary>
		protected Dictionary<string, Enum> mEnums = new Dictionary<string,Enum>(512);
		/// <summary>
		/// Excluded enumerations.
		/// </summary>
		private static Dictionary<string, bool> sEnumExclude = new Dictionary<string,bool>();
		/// <summary>
		/// Functions parsed
		/// </summary>
		protected Dictionary<string, Function> mFunctions = new Dictionary<string,Function>(512);
		/// <summary>
		/// Renamed function.
		/// </summary>
		private static Dictionary<string, string> sFunctRename = new Dictionary<string,string>();
		/// <summary>
		/// Excluded function.
		/// </summary>
		private static Dictionary<string, bool> sFunctExclude = new Dictionary<string,bool>();

		#endregion
	}
}